"""Install a Python package for the executing interpreter from local disk into
the interpreter's user site directory.

The installation process makes sure that the installation is done from a clean
copy of the package. This prevents extension modules and such from not being
rebuilt when the container is updated.

Containers are only supported if they allow for installing into the user site
directory for an interpreter. The currently support package containers are:

    * hg repositories
    * svn repositories
    * .zip files
    * .tar.gz files

"""
#TODO
#    * Prevent install attempt for Python versions that do not support user site directories.
#    * Archive support
#      + pip (easy_install doesn't support '--user')
#    * Automatically do a breadth-first search for setup.py for all container
#      types (I blame Distribute for this need =). Should remove need for
#      sub_directory attr.


import contextlib
import os
import shutil
import subprocess
import sys
import tarfile
import tempfile
import zipfile


@contextlib.contextmanager
def temp_directory():
    temp_dir = tempfile.mkdtemp()
    try:
        yield temp_dir
    finally:
        shutil.rmtree(temp_dir)


class Hg(object):

    update = ['hg', '-R', True, 'pull', '-u']
    unpack = ['hg', 'clone', '-u', '.', True, False]

    @staticmethod
    def detect(package):
        return os.path.isdir(os.path.join(package, '.hg'))


class Svn(object):

    update = ['svn', 'update', True]
    unpack = ['svn', 'export', '--force', True, False]

    @staticmethod
    def detect(package):
        return os.path.isdir(os.path.join(package, '.svn'))


class TarGz(object):

    unpack = ['tar', '-x', '-z', '-f', True, '-C', False]
    sub_directory = True

    @staticmethod
    def detect(package):
        # XXX Will mis-detect tar.bz2 files; might need unpack to be callable
        return tarfile.is_tarfile(package)


class Zip(object):

    unpack = ['unzip', True, '-d', False]
    sub_directory = True

    @staticmethod
    def detect(package):
        return zipfile.is_zipfile(package)


containers = (Hg, Svn, Zip, TarGz)


if len(sys.argv) != 2:
    raise ValueError("expected 1 command-line argument , given " +
                        str(len(sys.argv)-1))

package = sys.argv[1]

# Find the container format
for container in containers:
    if container.detect(package):
        break
else:
    sys.stdout.write("unsupported container")
    sys.exit(1)

# Update package, if possible
if hasattr(container, 'update'):
    command = list(container.update)
    command[command.index(True)] = package
    subprocess.check_call(command)

# In a temp directory ...
with temp_directory() as temp_dir:
    # Create a pristine copy of the package
    command = list(container.unpack)
    command[command.index(True)] = package
    command[command.index(False)] = temp_dir
    subprocess.check_call(command)

    # Install
    os.chdir(temp_dir)
    if hasattr(container, 'sub_directory') and container.sub_directory:
        contents = os.listdir('.')
        if len(contents) > 1:
            raise ValueError("cannot detect directory to change into")
        os.chdir(contents.pop())
    subprocess.check_call([sys.executable, 'setup.py', 'install', '--user'])
